#pragma once
#include <brpc/channel.h>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <unordered_map>
#include "../logger.hpp"


namespace shared {

namespace infra {
    
class RpcServiceChannel;
class RpcServiceManager;
using RpcServiceChannelPtr = std::shared_ptr<RpcServiceChannel>;
using RpcServiceManagerPtr = std::shared_ptr<RpcServiceManager>;

/* =========================================
 *   Rpc单个服务的信道管理类
 *   直接面向该服务创建出来的信道进行管理
* =========================================*/
class RpcServiceChannel
{
public:
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
public:
    RpcServiceChannel(const std::string& name)
        : serviceName_(name), index_(0) {}
        
    // 服务上线节点
    void append(const std::string& host)
    {
        ChannelPtr channel = std::make_shared<brpc::Channel>();
        brpc::ChannelOptions options;
        options.connect_timeout_ms = -1;
        options.timeout_ms = -1;
        options.max_retry = 3;
        options.protocol = "baidu_std";
        int ret = channel->Init(host.data(), &options);
        if (ret == -1) {
            LOG_ERROR("初始化{}-{}信道失败!", serviceName_, host);
            return;
        }
        std::lock_guard<std::mutex> locker(mutex_);
        channels_.emplace_back(channel);
        hosts_.emplace(host, channel);
    }
    // 服务下线节点
    void remove(const std::string &host)
    {
        std::unique_lock<std::mutex> lock(mutex_);
        if(!hosts_.count(host)) {
            LOG_WARN("{}-{} 该节点没有匹配的信道", serviceName_, host);
            return;
        }
        ChannelPtr channel = hosts_[host];
        for(auto it = channels_.begin(); it != channels_.end(); ++it) {
            if(*it == channel) {
                channels_.erase(it);
                break;
            }
        }
        hosts_.erase(host);
    }   
    // 通过RR轮转, 获取一个信道用于发起 rpc 请求
    ChannelPtr choose()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        if (channels_.size() == 0) {
            LOG_ERROR("没有节点提供 {} 服务", serviceName_);
            return ChannelPtr();
        }
        int32_t idx = index_++ % channels_.size();
        return channels_[idx];
    }

private:
    std::mutex mutex_;
    int32_t index_;                 // 轮转信道表的下标
    std::string serviceName_;       // 服务名称
    std::vector<ChannelPtr> channels_;  
    std::unordered_map<std::string, ChannelPtr> hosts_;    // <host,channel>
}; 


/* =========================================
 * Rpc所有服务的管理类
 *    不直接面向信道的管理, 而是只面向每一个rpc服务
      由每个服务句柄再去面向信道管理
* =========================================*/
class RpcServiceManager
{
public:
    RpcServiceManager() {}
    // 获取指定服务的 "节点信道"
    RpcServiceChannel::ChannelPtr choose(const std::string& serviceName) 
    {
        std::lock_guard<std::mutex> locker(mutex_);
        auto it = services_.find(serviceName);
        if(it == services_.end()) {
            LOG_WARN("没有节点提供 {} 服务", serviceName);
            return RpcServiceChannel::ChannelPtr();
        }
        return it->second->choose();
    }
    // 声明关注的服务
    void declared(const std::string& serviceName)
    {
        std::lock_guard<std::mutex> locker(mutex_);
        focusServices_.emplace(serviceName);
    }
    // 服务上线回调接口
    void onServiceOnline(const std::string& serviceInstance, const std::string& host)
    {
        const std::string serviceName = getServiceName(serviceInstance);
        RpcServiceChannelPtr service;
        {
            std::lock_guard<std::mutex> locker(mutex_);
            if(!focusServices_.count(serviceName)) {
                LOG_DEBUG("上线了一个没有关注的服务, {}-{}", serviceName, host);
                return;
            }
            auto it = services_.find(serviceName);
            if(it == services_.end()) {
                service = std::make_shared<RpcServiceChannel>(serviceName);
                services_.emplace(serviceName, service);
            } else {
                service = it->second;
            }
        }
        if(!service) {
            LOG_ERROR("新增{}服务异常!", serviceName);
            return;
        }
        service->append(host); 
        LOG_DEBUG("{}-{} 服务上线新节点", serviceName, host);
    }
    // 服务下线回调接口
    void onServiceOffline(const std::string& serviceInstance, const std::string& host)
    {
        const std::string serviceName = getServiceName(serviceInstance);
        RpcServiceChannelPtr service;
        {
            std::lock_guard<std::mutex> locker(mutex_);
            if(!focusServices_.count(serviceName)) {
                LOG_DEBUG("下线了一个没有关注的服务, {}-{}", serviceName, host);
                return;
            }
            auto it = services_.find(serviceName);
            if(it == services_.end()) {
                LOG_WARN("没有为该节点创建过服务, 下线异常");
                return;
            }
            service = it->second;
        }
        service->remove(host);   // 在该服务对象内, 把host节点下了
        LOG_DEBUG("{}-{} 服务下线节点", serviceName, host);
    }
private:
    std::string getServiceName(const std::string& serviceInstance) 
    {
        int pos = serviceInstance.find_last_of('/');
        return serviceInstance.substr(0, pos);
    }
private:
    std::mutex mutex_;
    std::unordered_set<std::string> focusServices_;
    std::unordered_map<std::string, RpcServiceChannelPtr> services_;  // <name, rpcService>
};


}}

